#include "Triangle.h"

Symbol WTriangle::parse(ifstream &file, Symbol curToken) {
    float *numFill;
    vec4 nPrime;

    curToken = matchToken(file, curToken, LBrace);

    numFill = parseVector(file, curToken, &curToken, 3);
    p1 = vec3(numFill[0], numFill[1], numFill[2]);
    delete numFill;

    curToken = matchToken(file, curToken, Comma);
    numFill = parseVector(file, curToken, &curToken, 3);
    p2 = vec3(numFill[0], numFill[1], numFill[2]);
    delete numFill;

    curToken = matchToken(file, curToken, Comma);
    numFill = parseVector(file, curToken, &curToken, 3);
    p3 = vec3(numFill[0], numFill[1], numFill[2]);
    delete numFill;
    
    curToken = parseMaterials(file, curToken, this);
    normal = cross(p2 - p1, p3 - p1);
    nPrime = vec4(normal.x, normal.y, normal.z, 0.0);
    nPrime = transpose(transform) * nPrime;
    normal = normalize(vec3(nPrime.x, nPrime.y, nPrime.z));

    return curToken;
}

vec3 WTriangle::genRandomPoint(vec3 look, int cur, int lim) {
    float alpha, beta;
    int split = (int) sqrt((float) lim) + 1;
    int alphaLevel = cur / split;
    int betaLevel = cur % split;

    vec4 point;
    vec3 p;

    //alpha = ((float) rand()) / RAND_MAX;
    alpha = (alphaLevel + 0.5f) / split;
    //beta = (1.f - alpha) * ((float) rand()) / RAND_MAX;
    beta = (1.f - alpha) * (betaLevel + 0.5f) / split;

    p = alpha * p1 + beta * p2 + (1.f - alpha - beta) * p3;
    point.w = 1.0;
    point.x = p.x;
    point.y = p.y;
    point.z = p.z;

    point = transpose(transform) * point;
    return vec3(point.x, point.y, point.z);
}

bool WTriangle::intersect(Ray *r) {
    mat3 A;
    vec3 b;
    vec3 x;
    vec3 d, point;
    vec4 dprime, pprime;
    float alpha, beta, gamma, t;
    float det;
    float m1, m2, m3;
    
    pprime = transform * vec4(r->point.x, r->point.y, r->point.z, 1.0);
    dprime = transform * vec4(r->d.x, r->d.y, r->d.z, 0.0);
    point = vec3(pprime.x, pprime.y, pprime.z);
    d = vec3(dprime.x, dprime.y, dprime.z);


    A[0][0] = p1.x - p2.x;
    A[0][1] = p1.y - p2.y;
    A[0][2] = p1.z - p2.z;
    A[1][0] = p1.x - p3.x;
    A[1][1] = p1.y - p3.y;
    A[1][2] = p1.z - p3.z;
    A[2][0] = d.x;
    A[2][1] = d.y;
    A[2][2] = d.z;

    b = p1 - point;

    x = inverse(A) * b;

/*    det = (p1.x - p2.x) * ((p1.y - p3.y) * d.z + d.y * (p3.z - p1.z)) +
          (p3.x - p1.x) * (d.z * (p1.y - p2.y) + d.y * (p2.z - p1.z)) +
          d.x * ((p1.y - p2.y) * (p1.z - p3.z) + (p3.y - p1.y) * (p1.z- p2.z));

    m1 = (p1.x - p3.x) * d.y - d.x * (p1.y - p3.y);
    m2 = -((p1.x - p2.x) * d.y - d.x * (p1.y - p2.y));
    m3 = ((p1.x - p2.x) * (p1.y - p3.y) - (p1.x - p3.x) * (p1.y - p2.y));*/
    /*t = (((p1.x - p3.x) * d.y + d.x * (p3.y - p1.y)) * (p1.x - point.x) +
        (d.x * (p1.y - p2.y) + (p2.x - p1.x) * d.y) * (p1.y - point.y) +
        ((p1.x - p2.x) * (p1.y - p3.y) + (p3.x - p1.x) * (p1.y - p2.y)) *
         (p1.z - point.z)) / det;*/
/*    t = m1 * (p1.x - point.x) + m2 * (p1.y - point.y) + m3 * (p1.z - point.z);
    t = t / det;*/


    t = x[2];
    if (t < 0.0 || t > r->t)
        return false;

    beta = x[0];
    /*beta = 
     (((p1.y - p3.y) * d.z - d.y * (p3.z - p1.z)) * (p1.x - point.x) +
     (d.y * (p2.z - p1.z) + (p2.y - p1.y) * d.z) * (p1.y - point.y) +
     ((p1.y - p2.y) * (p1.z - p3.z) + (p3.y - p1.y) * (p1.z - p2.z)) *
      (p1.z - point.z)) / det;*/

    if (beta < -0.02 || beta > 1.02) {
        return false;
    }

    gamma = x[1];
    /*gamma =
     (((p3.x - p1.x) * d.z + d.x * (p1.z - p3.z)) * (p1.x - point.x) +
     ((p1.x - p2.x) * d.z - d.x * (p1.z - p2.z)) * (p1.y - point.y) +
     ((p1.x - p2.x) * (p1.z - p3.z) + (p3.x - p1.x) * (p1.z - p2.z)) *
      (point.z - p1.z)) / det;*/

    if (gamma < -0.02 || gamma > 1.02) {
        return false;
    }

    alpha = 1 - beta - gamma;

    if (alpha < -0.02 || alpha > 1.02) {
        return false;
    }

    r->t = t;
    return true;
}

vec3 WTriangle::getNormal(Ray *r) {
    vec3 n = normal;
    vec3 noiseVec, d;
    float x, y;
    vec3 p = r->point + r->t * r->d;
    vec4 pPrime, dPrime;

    pPrime = transform * vec4(r->point.x, r->point.y, r->point.z, 1.0);
    dPrime = transform * vec4(r->d.x, r->d.y, r->d.z, 0.0);
    p = vec3(pPrime.x, pPrime.y, pPrime.z);
    d = vec3(dPrime.x, dPrime.y, dPrime.z);
    p = p + r->t * d;

    noiseVec = p - p1;
    switch ((int) finish.bump) {
        case 1:
            noiseVec = perlin1(100 * noiseVec.x, 100 * noiseVec.y, p, noise);
            break;
        case 2:
            noiseVec = perlin2(100 * noiseVec.x, 100 * noiseVec.y, p, noise);
            break;
        case 3:
            noiseVec = perlin3(100 * noiseVec.x, 100 * noiseVec.y, p, noise);
            break;
        default:
            noiseVec = vec3(0);
            break;
            
    }

    return normalize(normal + noiseVec);
}

AABB *WTriangle::generateAABB() {
    AABB *ret;

    ret = new AABB();
    ret->g = this;
    ret->isLeaf = true;

    ret->min = vec3(
            std::min(std::min(p1.x, p2.x), p3.x) - 0.01,
            std::min(std::min(p1.y, p2.y), p3.y) - 0.01,
            std::min(std::min(p1.z, p2.z), p3.z) - 0.01);

    ret->max = vec3(
            std::max(std::max(p1.x, p2.x), p3.x) + 0.01,
            std::max(std::max(p1.y, p2.y), p3.y) + 0.01,
            std::max(std::max(p1.z, p2.z), p3.z) + 0.01);


    fix_AABB_transform(ret, vtrans);

    return ret;
}
